Mestre React Suspense med praktiske mønstre for effektiv datahenting, lastetilstander og robust feilhåndtering. Bygg jevnere og mer robuste brukeropplevelser.
React Suspense-mønstre: Datahenting og feilgrenser
React Suspense er en kraftig funksjon som lar deg "suspendere" komponentgjengivelse mens du venter på asynkrone operasjoner, som for eksempel datahenting, å fullføres. Sammen med feilgrenser gir det en robust mekanisme for å håndtere lastetilstander og feil, noe som resulterer i en jevnere og mer robust brukeropplevelse. Denne artikkelen utforsker ulike mønstre for å utnytte Suspense og feilgrenser effektivt i React-applikasjonene dine.
Forstå React Suspense
I kjernen er Suspense en mekanisme som lar React vente på noe før en komponent gjengis. Dette "noe" er vanligvis en asynkron operasjon, som å hente data fra et API. I stedet for å vise en blank skjerm eller en potensielt misvisende mellomtilstand, kan du vise et fallback-UI (f.eks. en lastesnurr) mens dataene lastes.
Den viktigste fordelen er forbedret opplevd ytelse og en mer behagelig brukeropplevelse. Brukere presenteres umiddelbart med visuell tilbakemelding som indikerer at noe skjer, i stedet for å lure på om applikasjonen er frosset.
Nøkkelkonsepter
- Suspense-komponent:
<Suspense>-komponenten pakker inn komponenter som kan suspendere. Den aksepterer enfallback-prop, som spesifiserer UI-et som skal gjengis mens de innpakkede komponentene er suspendert. - Fallback-UI: Dette er UI-et som vises mens den asynkrone operasjonen pågår. Det kan være alt fra en enkel lastesnurr til en mer forseggjort animasjon.
- Promise-integrasjon: Suspense fungerer med Promises. Når en komponent prøver å lese en verdi fra en Promise som ikke er løst ennå, suspenderer React komponenten og viser fallback-UI-et.
- Datakilder: Suspense er avhengig av datakilder som er Suspense-aware. Disse kildene eksponerer et API som lar React oppdage når data hentes.
Hente data med Suspense
For å bruke Suspense for datahenting, trenger du et Suspense-aware datahentingsbibliotek. Her er en vanlig tilnærming ved hjelp av en tilpasset `fetchData`-funksjon:
Eksempel: Enkel datahenting
Først, lag en hjelpefunksjon for å hente data. Denne funksjonen må håndtere 'suspending'-aspektet. Vi vil pakke våre fetch-kall i en tilpasset ressurs for å håndtere promise-tilstanden korrekt.
// utils/api.js
const wrapPromise = (promise) => {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
}
return result;
},
};
};
const fetchData = (url) => {
const promise = fetch(url)
.then((res) => res.json())
.then((data) => data);
return wrapPromise(promise);
};
export default fetchData;
La oss nå lage en komponent som bruker Suspense for å vise brukerdata:
// components/UserProfile.js
import React from 'react';
import fetchData from '../utils/api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
export default UserProfile;
Til slutt, pakk UserProfile-komponenten med <Suspense>:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
function App() {
return (
<Suspense fallback={<p>Laster brukerdata...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
I dette eksemplet prøver UserProfile-komponenten å lese user-dataene fra resource. Hvis dataene ennå ikke er tilgjengelige (Promise er fortsatt ventende), suspenderer komponenten, og fallback-UI-et ("Laster brukerdata...") vises. Når dataene er hentet, gjengis komponenten på nytt med den faktiske brukerinformasjonen.
Fordeler med denne tilnærmingen
- Deklarativ datahenting: Komponenten uttrykker *hvilke* data den trenger, ikke *hvordan* den skal hente dem.
- Sentralisert håndtering av lastetilstand: Suspense-komponenten håndterer lastetilstanden, noe som forenkler komponentlogikken.
Feilgrenser for robusthet
Mens Suspense håndterer lastetilstander på en elegant måte, håndterer den ikke iboende feil som kan oppstå under datahenting eller komponentgjengivelse. Det er her feilgrenser kommer inn.
Feilgrenser er React-komponenter som fanger JavaScript-feil hvor som helst i deres underkomponenttre, logger disse feilene og viser et fallback-UI i stedet for å krasje hele applikasjonen. De er avgjørende for å bygge robuste brukergrensesnitt som kan håndtere uventede feil på en elegant måte.
Opprette en feilgrense
For å opprette en feilgrense, må du definere en klassekomponent som implementerer static getDerivedStateFromError()- og componentDidCatch()-livssyklusmetodene.
// components/ErrorBoundary.js
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error("Caught error: ", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<div>
<h2>Noe gikk galt.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
getDerivedStateFromError-metoden kalles når en feil kastes i en etterkommerkomponent. Den oppdaterer tilstanden for å indikere at en feil har oppstått.
componentDidCatch-metoden kalles etter at en feil er kastet. Den mottar feilen og feilinformasjonen, som du kan bruke til å logge feilen til en feilrapporteringstjeneste eller vise en mer informativ feilmelding.
Bruke feilgrenser med Suspense
For å kombinere feilgrenser med Suspense, er det bare å pakke <Suspense>-komponenten med en <ErrorBoundary>-komponent:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
import ErrorBoundary from './components/ErrorBoundary';
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Laster brukerdata...</p>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}
export default App;
Nå, hvis en feil oppstår under datahenting eller gjengivelse av UserProfile-komponenten, vil feilgrensen fange feilen og vise fallback-UI-et, og forhindre at hele applikasjonen krasjer.
Avanserte Suspense-mønstre
Utover grunnleggende datahenting og feilhåndtering, tilbyr Suspense flere avanserte mønstre for å bygge mer sofistikerte brukergrensesnitt.
Kodesplitting med Suspense
Kodesplitting er prosessen med å dele opp applikasjonen din i mindre biter som kan lastes inn ved behov. Dette kan forbedre den første innlastingstiden for applikasjonen din betydelig.
React.lazy og Suspense gjør kodesplitting utrolig enkelt. Du kan bruke React.lazy til å importere komponenter dynamisk, og deretter pakke dem med <Suspense> for å vise et fallback-UI mens komponentene lastes inn.
// components/MyComponent.js
import React from 'react';
const MyComponent = React.lazy(() => import('./AnotherComponent'));
function App() {
return (
<Suspense fallback={<p>Laster komponent...</p>}>
<MyComponent />
</Suspense>
);
}
export default App;
I dette eksemplet lastes MyComponent inn ved behov. Mens den lastes, vises fallback-UI-et ("Laster komponent..."). Når komponenten er lastet inn, gjengis den normalt.
Parallell datahenting
Suspense lar deg hente flere datakilder parallelt og vise et enkelt fallback-UI mens alle dataene lastes inn. Dette kan være nyttig når du trenger å hente data fra flere API-er for å gjengi en enkelt komponent.
import React, { Suspense } from 'react';
import fetchData from './api';
const userResource = fetchData('https://jsonplaceholder.typicode.com/users/1');
const postsResource = fetchData('https://jsonplaceholder.typicode.com/posts?userId=1');
function UserProfile() {
const user = userResource.read();
const posts = postsResource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<h3>Innlegg:</h3>
<ul>
{posts.map(post => (<li key={post.id}>{post.title}</li>))}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback={<p>Laster brukerdata og innlegg...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
I dette eksemplet henter UserProfile-komponenten både brukerdata og innleggsdata parallelt. <Suspense>-komponenten viser et enkelt fallback-UI mens begge datakildene lastes inn.
Transition API med useTransition
React 18 introduserte useTransition-hooken, som forbedrer Suspense ved å gi en måte å administrere UI-oppdateringer som overganger. Dette betyr at du kan markere visse tilstandsoppdateringer som mindre presserende og hindre dem i å blokkere UI-et. Dette er spesielt nyttig når du arbeider med tregere datahenting eller komplekse gjengivelsesoperasjoner, noe som forbedrer opplevd ytelse.
Her er hvordan du kan bruke useTransition:
import React, { useState, Suspense, useTransition } from 'react';
import fetchData from './api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
function App() {
const [isPending, startTransition] = useTransition();
const [showProfile, setShowProfile] = useState(false);
const handleClick = () => {
startTransition(() => {
setShowProfile(true);
});
};
return (
<div>
<button onClick={handleClick} disabled={isPending}>
Vis brukerprofil
</button>
{isPending && <p>Laster...</p>}
<Suspense fallback={<p>Laster brukerdata...</p>}>
{showProfile && <UserProfile />}
</Suspense>
</div>
);
}
export default App;
I dette eksemplet initierer et klikk på "Vis brukerprofil"-knappen en overgang. startTransition markerer setShowProfile-oppdateringen som en overgang, slik at React kan prioritere andre UI-oppdateringer. isPending-verdien fra useTransition indikerer om en overgang pågår, slik at du kan gi visuell tilbakemelding (f.eks. deaktivere knappen og vise en lastemelding).
Beste praksis for bruk av Suspense og feilgrenser
- Pakk Suspense rundt det minste mulige området: Unngå å pakke store deler av applikasjonen din med
<Suspense>. Pakk i stedet bare komponentene som faktisk trenger å suspendere. Dette vil minimere innvirkningen på resten av UI-et. - Bruk meningsfulle fallback-UI-er: Fallback-UI-et bør gi brukerne klar og informativ tilbakemelding om hva som skjer. Unngå generiske lastesnurrer; prøv i stedet å gi mer kontekst (f.eks. "Laster brukerdata...").
- Plasser feilgrenser strategisk: Tenk nøye over hvor du skal plassere feilgrenser. Plasser dem høyt nok i komponenttreet til å fange feil som kan påvirke flere komponenter, men lavt nok til å unngå å fange feil som er spesifikke for en enkelt komponent.
- Logg feil: Bruk
componentDidCatch-metoden til å logge feil til en feilrapporteringstjeneste. Dette vil hjelpe deg med å identifisere og fikse feil i applikasjonen din. - Gi brukervennlige feilmeldinger: Fallback-UI-et som vises av feilgrenser, bør gi brukerne nyttig informasjon om feilen og hva de kan gjøre med den. Unngå teknisk sjargong; bruk i stedet et klart og konsist språk.
- Test feilgrensene dine: Sørg for at feilgrensene dine fungerer korrekt ved å bevisst kaste feil i applikasjonen din.
Internasjonale hensyn
Når du bruker Suspense og feilgrenser i internasjonale applikasjoner, bør du vurdere følgende:
- Lokalisering: Sørg for at fallback-UI-ene og feilmeldingene er riktig lokalisert for hvert språk som støttes av applikasjonen din. Bruk internasjonaliseringsbiblioteker (i18n) som
react-intlelleri18nextfor å administrere oversettelser. - Høyre-til-venstre (RTL) layouter: Hvis applikasjonen din støtter RTL-språk (f.eks. arabisk, hebraisk), må du sørge for at fallback-UI-ene og feilmeldingene vises riktig i RTL-layouter. Bruk CSS logiske egenskaper (f.eks.
margin-inline-starti stedet formargin-left) for å støtte både LTR- og RTL-layouter. - Tilgjengelighet: Sørg for at fallback-UI-ene og feilmeldingene er tilgjengelige for brukere med funksjonshemninger. Bruk ARIA-attributter for å gi semantisk informasjon om lastetilstanden og feilmeldingene.
- Kulturell sensitivitet: Vær oppmerksom på kulturelle forskjeller når du designer fallback-UI-er og feilmeldinger. Unngå å bruke bilder eller språk som kan være støtende eller upassende i visse kulturer. For eksempel kan en vanlig lastesnurr oppfattes negativt i noen kulturer.
Eksempel: Lokalisert feilmelding
Ved hjelp av react-intl kan du opprette lokaliserte feilmeldinger:
// components/ErrorBoundary.js
import React from 'react';
import { FormattedMessage } from 'react-intl';
class ErrorBoundary extends React.Component {
// ... (samme som før)
render() {
if (this.state.hasError) {
return (
<div>
<h2><FormattedMessage id="error.title" defaultMessage="Something went wrong." /></h2>
<p><FormattedMessage id="error.message" defaultMessage="Please try again later." /></p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Definer deretter oversettelsene i dine lokale filer:
// locales/en.json
{
"error.title": "Something went wrong.",
"error.message": "Please try again later."
}
// locales/fr.json
{
"error.title": "Quelque chose s'est mal passé.",
"error.message": "Veuillez réessayer plus tard."
}
Konklusjon
React Suspense og feilgrenser er viktige verktøy for å bygge moderne, robuste og brukervennlige brukergrensesnitt. Ved å forstå og anvende mønstrene som er beskrevet i denne artikkelen, kan du forbedre den opplevde ytelsen og den generelle kvaliteten på React-applikasjonene dine betydelig. Husk å vurdere internasjonalisering og tilgjengelighet for å sikre at applikasjonene dine er brukbare for et globalt publikum.
Asynkron datahenting og riktig feilhåndtering er kritiske aspekter ved enhver webapplikasjon. Suspense, kombinert med feilgrenser, tilbyr en deklarativ og effektiv måte å håndtere disse kompleksitetene i React, noe som resulterer i en jevnere og mer pålitelig brukeropplevelse for brukere over hele verden.